package org.litepal.crud;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;

import org.litepal.LitePalBase;
import org.litepal.crud.model.AssociationInfo;
import org.litepal.exceptions.DataSupportException;
import org.litepal.util.Const;

/**
 * Deals with many2one condition when comes to two models are associated.
 * 
 * @author Tony Green
 * @since 1.1
 */
class Many2OneAnalyzer extends AssociationsAnalyzer {

	/**
	 * Analyzing the RelatedInfo. It will help baseObj assign the necessary
	 * values automatically. If the two associated models has both way
	 * association but developer has only build one way association in model, it
	 * will force to build the both way association. Besides the
	 * {@link DataSupport#associatedModels} will be found and put here.
	 * 
	 * @param baseObj
	 *            The baseObj currently want to persist.
	 * @param associationInfo
	 *            The associated info analyzed by
	 *            {@link LitePalBase#getRelatedInfo}.
	 * @throws SecurityException
	 * @throws IllegalArgumentException
	 * @throws NoSuchMethodException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	@SuppressWarnings("unchecked")
	void analyze(DataSupport baseObj, AssociationInfo associationInfo) throws SecurityException,
			IllegalArgumentException, NoSuchMethodException, IllegalAccessException,
			InvocationTargetException {
		if (getBaseClassName(baseObj).equals(associationInfo.getClassHoldsForeignKey())) {
			DataSupport associatedModel = (DataSupport) takeGetMethodValueByField(baseObj,
					associationInfo.getAssociateOtherModelFromSelf());
			if (associatedModel != null) {
				Collection<DataSupport> associatedModels = (Collection<DataSupport>) takeGetMethodValueByField(
						associatedModel, associationInfo.getAssociateSelfFromOtherModel());
				if (associatedModels == null) {
					if (isList(associationInfo.getAssociateSelfFromOtherModel().getType())) {
						associatedModels = new ArrayList<DataSupport>();
					} else if (isSet(associationInfo.getAssociateSelfFromOtherModel().getType())) {
						associatedModels = new HashSet<DataSupport>();
					} else {
						throw new DataSupportException(
								DataSupportException.WRONG_FIELD_TYPE_FOR_ASSOCIATIONS);
					}
				}
				associatedModels.add(baseObj);
				baseObj.addNewAssociatedModel(associatedModel, Const.Model.MANY_TO_ONE, true);
				associatedModel.addNewAssociatedModel(baseObj, Const.Model.MANY_TO_ONE, false);
			}
		} else {
			Collection<DataSupport> getMethodValue = (Collection<DataSupport>) takeGetMethodValueByField(
					baseObj, associationInfo.getAssociateOtherModelFromSelf());
			if (getMethodValue != null) {
				for (DataSupport associatedModel : getMethodValue) {
					putSetMethodValueByField(associatedModel,
							associationInfo.getAssociateSelfFromOtherModel(), baseObj);
					baseObj.addNewAssociatedModel(associatedModel, Const.Model.MANY_TO_ONE, false);
					associatedModel.addNewAssociatedModel(baseObj, Const.Model.MANY_TO_ONE, true);
				}
			}
		}
	}

}
